`timescale 1ns / 1ps
/*--------------------------------------------------------------------*\
FileName        : hpm_dpram.v
Author          : hpy
Email           : yuan_hp@qq.com
Date            : 2025.06.26
Description     : 多平台dpram


1.例化示例 
hpm_dpram #(
    .PLAT(""),        // platform  "quartus" "vivado" "lattice"  none:verilog 
    .DATA_NUM(8),     // 数据存储个数
    .ADDR_WIDTH(3),   // 地址位宽
    .DATA_WIDTH(8),   // 输入数据位宽
	.OUTPUT_REG(1)   // 输出是否有reg，1有，0没有，默认1
) u_hpm_dpram (
    .address_a(),  // input[ADDR_WIDTH-1:0]
    .address_b(),
    .clock(),    // input[0:0] 
    .data_a(),     // input[DATA_WIDTH-1:0]
    .data_b(),
    .wren_a(),     // input[0:0] 
    .wren_b(),
    .q_a(),
    .q_b()
);
\*--------------------------------------------------------------------*/

module hpm_dpram #(
    parameter  PLAT = "", // 平台 "quartus" "vivado" "lattice"
    parameter  INIT_MEM_FILE = "init.mem", // memory init file , *.mem 文件
    parameter  DATA_NUM = 256 ,
    parameter  ADDR_WIDTH = 8,
    parameter  DATA_WIDTH = 8,
	parameter  OUTPUT_REG = 1
)(
	address_a,
	address_b,
	clock,
	data_a,
	data_b,
	wren_a,
	wren_b,
	q_a,
	q_b);

	input	[7:0]  address_a;
	input	[7:0]  address_b;
	input	  clock;
	input	[7:0]  data_a;
	input	[7:0]  data_b;
	input	  wren_a;
	input	  wren_b;
	output	[7:0]  q_a;
	output	[7:0]  q_b;

localparam Q_REG = |OUTPUT_REG ? "CLOCK0" : "UNREGISTERED"  ;
localparam READ_LATENCY = |OUTPUT_REG ? 1 : 0;
localparam pmi_regmode = |OUTPUT_REG ? "reg":"noreg" ;

generate 
    if(PLAT=="quartus") begin
        wire [7:0] sub_wire0;
        wire [7:0] sub_wire1;
        assign q_a = sub_wire0[7:0];
        assign q_b = sub_wire1[7:0];

        altsyncram	altsyncram_component (
                    .address_a (address_a),
                    .address_b (address_b),
                    .clock0 (clock),
                    .data_a (data_a),
                    .data_b (data_b),
                    .wren_a (wren_a),
                    .wren_b (wren_b),
                    .q_a (sub_wire0),
                    .q_b (sub_wire1),
                    .aclr0 (1'b0),
                    .aclr1 (1'b0),
                    .addressstall_a (1'b0),
                    .addressstall_b (1'b0),
                    .byteena_a (1'b1),
                    .byteena_b (1'b1),
                    .clock1 (1'b1),
                    .clocken0 (1'b1),
                    .clocken1 (1'b1),
                    .clocken2 (1'b1),
                    .clocken3 (1'b1),
                    .eccstatus (),
                    .rden_a (1'b1),
                    .rden_b (1'b1));
        defparam
            altsyncram_component.address_reg_b = "CLOCK0",
            altsyncram_component.init_file = INIT_MEM_FILE ,
            altsyncram_component.clock_enable_input_a = "BYPASS",
            altsyncram_component.clock_enable_input_b = "BYPASS",
            altsyncram_component.clock_enable_output_a = "BYPASS",
            altsyncram_component.clock_enable_output_b = "BYPASS",
            altsyncram_component.indata_reg_b = "CLOCK0",
            altsyncram_component.intended_device_family = "Cyclone IV E",
            altsyncram_component.lpm_type = "altsyncram",
            altsyncram_component.numwords_a = DATA_NUM,
            altsyncram_component.numwords_b = DATA_NUM,
            altsyncram_component.operation_mode = "BIDIR_DUAL_PORT",
            altsyncram_component.outdata_aclr_a = "NONE",
            altsyncram_component.outdata_aclr_b = "NONE",
            altsyncram_component.outdata_reg_a = Q_REG,
            altsyncram_component.outdata_reg_b = Q_REG,
            altsyncram_component.power_up_uninitialized = "FALSE",
            altsyncram_component.read_during_write_mode_mixed_ports = "DONT_CARE",
            altsyncram_component.read_during_write_mode_port_a = "NEW_DATA_WITH_NBE_READ",
            altsyncram_component.read_during_write_mode_port_b = "NEW_DATA_WITH_NBE_READ",
            altsyncram_component.widthad_a = ADDR_WIDTH,
            altsyncram_component.widthad_b = ADDR_WIDTH,
            altsyncram_component.width_a = DATA_WIDTH,
            altsyncram_component.width_b = DATA_WIDTH,
            altsyncram_component.width_byteena_a = 1,
            altsyncram_component.width_byteena_b = 1,
            altsyncram_component.wrcontrol_wraddress_reg_b = "CLOCK0";

    end else if(PLAT=="vivado") begin
        xpm_memory_tdpram #(
            .ADDR_WIDTH_A(ADDR_WIDTH),               // DECIMAL
            .ADDR_WIDTH_B(ADDR_WIDTH),               // DECIMAL
            .AUTO_SLEEP_TIME(0),            // DECIMAL
            .BYTE_WRITE_WIDTH_A(DATA_WIDTH),        // DECIMAL
            .BYTE_WRITE_WIDTH_B(DATA_WIDTH),        // DECIMAL
            .CLOCKING_MODE("common_clock"), // String
            .ECC_MODE("no_ecc"),            // String
            .MEMORY_INIT_FILE(|INIT_MEM_FILE ? INIT_MEM_FILE : "none"),      // String
            .MEMORY_INIT_PARAM("0"),        // String
            .MEMORY_OPTIMIZATION("true"),   // String
            .MEMORY_PRIMITIVE("auto"),      // String
            .MEMORY_SIZE(DATA_NUM*DATA_WIDTH),             // DECIMAL
            .MESSAGE_CONTROL(0),            // DECIMAL
            .READ_DATA_WIDTH_A(DATA_WIDTH),         // DECIMAL
            .READ_DATA_WIDTH_B(DATA_WIDTH),         // DECIMAL
            .READ_LATENCY_A(READ_LATENCY),             // DECIMAL
            .READ_LATENCY_B(READ_LATENCY),             // DECIMAL
            .READ_RESET_VALUE_A("0"),       // String
            .READ_RESET_VALUE_B("0"),       // String
            .RST_MODE_A("SYNC"),            // String
            .RST_MODE_B("SYNC"),            // String
            .USE_EMBEDDED_CONSTRAINT(0),    // DECIMAL
            .USE_MEM_INIT(1),               // DECIMAL
            .WAKEUP_TIME("disable_sleep"),  // String
            .WRITE_DATA_WIDTH_A(DATA_WIDTH),        // DECIMAL
            .WRITE_DATA_WIDTH_B(DATA_WIDTH),        // DECIMAL
            .WRITE_MODE_A("no_change"),     // String
            .WRITE_MODE_B("no_change")      // String
        )
        xpm_memory_tdpram_inst (
            .dbiterra(),             // 1-bit output: Status signal to indicate double bit error occurrence
                                            // on the data output of port A.

            .dbiterrb(),             // 1-bit output: Status signal to indicate double bit error occurrence
                                            // on the data output of port A.

            .douta(q_a),                   // READ_DATA_WIDTH_A-bit output: Data output for port A read operations.
            .doutb(q_b),                   // READ_DATA_WIDTH_B-bit output: Data output for port B read operations.
            .sbiterra(),             // 1-bit output: Status signal to indicate single bit error occurrence
                                            // on the data output of port A.

            .sbiterrb(),             // 1-bit output: Status signal to indicate single bit error occurrence
                                            // on the data output of port B.

            .addra(address_a),                   // ADDR_WIDTH_A-bit input: Address for port A write and read operations.
            .addrb(address_b),                   // ADDR_WIDTH_B-bit input: Address for port B write and read operations.
            .clka(clock),                     // 1-bit input: Clock signal for port A. Also clocks port B when
                                            // parameter CLOCKING_MODE is "common_clock".

            .clkb(clock),                     // 1-bit input: Clock signal for port B when parameter CLOCKING_MODE is
                                            // "independent_clock". Unused when parameter CLOCKING_MODE is
                                            // "common_clock".

            .dina(data_a),                     // WRITE_DATA_WIDTH_A-bit input: Data input for port A write operations.
            .dinb(data_b),                     // WRITE_DATA_WIDTH_B-bit input: Data input for port B write operations.
            .ena(1'b1),                       // 1-bit input: Memory enable signal for port A. Must be high on clock
                                            // cycles when read or write operations are initiated. Pipelined
                                            // internally.

            .enb(1'b1),                       // 1-bit input: Memory enable signal for port B. Must be high on clock
                                            // cycles when read or write operations are initiated. Pipelined
                                            // internally.

            .injectdbiterra(1'b1), // 1-bit input: Controls double bit error injection on input data when
                                            // ECC enabled (Error injection capability is not available in
                                            // "decode_only" mode).

            .injectdbiterrb(1'b1), // 1-bit input: Controls double bit error injection on input data when
                                            // ECC enabled (Error injection capability is not available in
                                            // "decode_only" mode).

            .injectsbiterra(1'b1), // 1-bit input: Controls single bit error injection on input data when
                                            // ECC enabled (Error injection capability is not available in
                                            // "decode_only" mode).

            .injectsbiterrb(1'b1), // 1-bit input: Controls single bit error injection on input data when
                                            // ECC enabled (Error injection capability is not available in
                                            // "decode_only" mode).

            .regcea(1'b1),                 // 1-bit input: Clock Enable for the last register stage on the output
                                            // data path.

            .regceb(1'b1),                 // 1-bit input: Clock Enable for the last register stage on the output
                                            // data path.

            .rsta(1'b0),                     // 1-bit input: Reset signal for the final port A output register stage.
                                            // Synchronously resets output port douta to the value specified by
                                            // parameter READ_RESET_VALUE_A.

            .rstb(1'b0),                     // 1-bit input: Reset signal for the final port B output register stage.
                                            // Synchronously resets output port doutb to the value specified by
                                            // parameter READ_RESET_VALUE_B.

            .sleep(),                   // 1-bit input: sleep signal to enable the dynamic power saving feature.
            .wea(wren_a),                       // WRITE_DATA_WIDTH_A-bit input: Write enable vector for port A input
                                            // data port dina. 1 bit wide when word-wide writes are used. In
                                            // byte-wide write configurations, each bit controls the writing one
                                            // byte of dina to address addra. For example, to synchronously write
                                            // only bits [15-8] of dina when WRITE_DATA_WIDTH_A is 32, wea would be
                                            // 4'b0010.

            .web(wren_b)                        // WRITE_DATA_WIDTH_B-bit input: Write enable vector for port B input
                                            // data port dinb. 1 bit wide when word-wide writes are used. In
                                            // byte-wide write configurations, each bit controls the writing one
                                            // byte of dinb to address addrb. For example, to synchronously write
                                            // only bits [15-8] of dinb when WRITE_DATA_WIDTH_B is 32, web would be
                                            // 4'b0010.

        );

    end else if( PLAT == "lattice") begin
        pmi_ram_dp_true #(
            .pmi_addr_depth_a(DATA_NUM),
            .pmi_addr_width_a(ADDR_WIDTH),
            .pmi_data_width_a(DATA_WIDTH),
            .pmi_addr_depth_b(DATA_NUM),
            .pmi_addr_width_b(ADDR_WIDTH),
            .pmi_data_width_b(DATA_WIDTH),

            .pmi_regmode_a (pmi_regmode),
            .pmi_regmode_b (pmi_regmode),
            .pmi_gsr ("disable"),
            .pmi_resetmode("sync"),
            .pmi_optimization ("speed"),
            .pmi_init_file (|INIT_MEM_FILE ? INIT_MEM_FILE : "none"),
            .pmi_init_file_format("hex"),
            .pmi_write_mode_a ("normal"),
            .pmi_write_mode_b ("normal"),
            .pmi_family ("EC"),
            .module_type ("pmi_ram_dp_true")
        ) u_pmi_ram_dp_true (
            .DataInA(data_a),
            .DataInB(data_b),
            .AddressA(address_a),
            .AddressB(address_b),
            .ClockA(clock),
            .ClockB(clock),
            .ClockEnA(1'b1),
            .ClockEnB(1'b1),
            .WrA(wren_a),
            .WrB(wren_b),
            .ResetA(1'b0),
            .ResetB(1'b0),
            .QA(q_a),
            .QB(q_b)
        )/*synthesis syn_black_box */;   
    end else begin
        reg [DATA_WIDTH - 1 : 0] mem [0:DATA_NUM] ;

        reg [DATA_WIDTH - 1 : 0] q_a_r;
        reg [DATA_WIDTH - 1 : 0] q_b_r;

        assign q_a = q_a_r ; 
        assign q_b = q_b_r ;

        initial begin
            if(|INIT_MEM_FILE) begin
                $readmemh(INIT_MEM_FILE,mem);
            end
        end
        
        always@(posedge clock) begin 
            if(wren_a == 1'b1) begin 
                mem[address_a] <= data_a;
            end  

            if(wren_b == 1'b1) begin 
                mem[address_b] <= data_b;
            end  
        end 

        if(|OUTPUT_REG) begin
            always@(posedge clock) begin 
                q_a_r <= mem[address_a];
                q_b_r <= mem[address_b];
            end 
        end else begin
            always@(address_a,clock) begin
                q_a_r = mem[address_a];
            end

            always@(address_b,clock) begin
                q_b_r = mem[address_b];
            end
        end
    
    end
endgenerate

endmodule 